/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.neo.drools.clear;

import com.alibaba.fastjson.JSON;
import com.neo.drools.entity.ProductInfo;
import com.neo.drools.entity.RuleDto;
import com.neo.drools.utils.DroolsUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.kie.api.event.rule.DebugAgendaEventListener;
import org.kie.api.event.rule.DebugRuleRuntimeEventListener;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.internal.utils.KieHelper;

import java.util.ArrayList;
import java.util.List;

/**
 * 数据清洗
 *
 * @author liaojinneng
 */
public class ClearDataExample {

    public static void main(final String[] args) {

        //executeStaticDrl(getProductInfoList(), getRuleDtoList());

        //executeDynamicDrl(getProductInfoList(), getRuleDtoList());

        executeStaticDrl(getProductInfoListTest(), getRuleDtoListTest());

    }

    /**
     * 查询待清洗数据
     * 实际生产环境为读数据库数据
     *
     * @return 待清洗数据
     */
    public static List<ProductInfo> getProductInfoListTest() {
        List<ProductInfo> productInfoList = new ArrayList<>();
        ProductInfo productInfo1 = new ProductInfo(1L, "三九牌 感冒颗粒 24包/盒 每粒装", "24包/盒 每粒装");
        productInfoList.add(productInfo1);
        return productInfoList;
    }

    /**
     * 查询清洗规则
     * 实际生产环境通过配置在apollo里面，或者读取数据库（开发一个清洗规则维护界面）
     *
     *
     * @return 清洗规则
     */
    public static List<RuleDto> getRuleDtoListTest() {
        List<RuleDto> ruleDtoList = new ArrayList<>();

        RuleDto ruleDto1 = new RuleDto(RuleDto.REPLACE, "包", "袋");
        //RuleDto ruleDto2 = new RuleDto(RuleDto.REPLACE, "袋", "dai");
        //RuleDto ruleDto3 = new RuleDto(RuleDto.REPLACE, "dai", "包");
        ruleDtoList.add(ruleDto1);
        //ruleDtoList.add(ruleDto2);
        //ruleDtoList.add(ruleDto3);
        return ruleDtoList;
    }

    /**
     * 使用drools进行数据清洗-读取项目中创建的drools规则文件的方式
     * 清洗规则通过配置在apollo里面，或者读取数据库（开发一个清洗规则维护界面,或者通过sql插入）
     *
     * @param productInfoList 待清洗产品
     * @param ruleDtoList     清洗规则
     */
    public static void executeStaticDrl(List<ProductInfo> productInfoList, List<RuleDto> ruleDtoList) {
        if (CollectionUtils.isEmpty(productInfoList) || CollectionUtils.isEmpty(ruleDtoList)) {
            return;
        }

        // 创建 KieSession
        KieSession ksession = DroolsUtils.getKieContainer().newKieSession("ClearDataKS");

        // 设置执行过程监听器(打印执行过程日志)
        ksession.addEventListener(new DebugAgendaEventListener());
        ksession.addEventListener(new DebugRuleRuntimeEventListener());

        // 将规则操作对象（fact）插入到session
        // 插入待清洗数据
        productInfoList.forEach(ksession::insert);
        // 插入规则
        ruleDtoList.forEach(ksession::insert);

        // 规则匹配和执行
        System.out.println("======清洗前productInfo数据:" + productInfoList.toString());
        ksession.fireAllRules();
        System.out.println("======清洗后productInfo数据:" + productInfoList.toString());

        // 销毁session
        ksession.dispose();
    }

    /**
     * 查询待清洗数据
     * 实际生产环境为读数据库数据
     *
     * @return 待清洗数据
     */
    public static List<ProductInfo> getProductInfoList() {
        List<ProductInfo> productInfoList = new ArrayList<>();
        ProductInfo productInfo1 = new ProductInfo(1L, "三九牌 感冒颗粒 24包/盒 每粒装", "24包/盒 每粒装");
        ProductInfo productInfo2 = new ProductInfo(2L, "三九牌 感冒颗粒-III 24包/盒 每粒装", "24包/盒 每粒装");
        productInfoList.add(productInfo1);
        productInfoList.add(productInfo2);
        return productInfoList;
    }

    /**
     * 查询清洗规则
     * 实际生产环境通过配置在apollo里面，或者读取数据库（开发一个清洗规则维护界面）
     *
     * @return 清洗规则
     */
    public static List<RuleDto> getRuleDtoList() {
        return getRuleDtoListFromApollo();

        /*List<RuleDto> ruleDtoList = new ArrayList<>();
        RuleDto ruleDto1 = new RuleDto(RuleDto.REPLACE, "包", "袋");
        RuleDto ruleDto2 = new RuleDto(RuleDto.REPLACE, "III", "Ⅲ");
        RuleDto ruleDto3 = new RuleDto(RuleDto.EMPTY, "每粒装", null);
        ruleDtoList.add(ruleDto1);
        ruleDtoList.add(ruleDto2);
        ruleDtoList.add(ruleDto3);
        return ruleDtoList;*/
    }


    /**
     * 从apollo读取清洗规则
     */
    public static List<RuleDto> getRuleDtoListFromApollo() {
        String clearRules =
                "[{\"rawValue\":\"包\",\"ruleType\":1,\"standardValue\":\"袋\"}," +
                "{\"rawValue\":\"III\",\"ruleType\":1,\"standardValue\":\"Ⅲ\"}," +
                "{\"rawValue\":\"每粒装\",\"ruleType\":2}]";
        if (StringUtils.isEmpty(clearRules)) {
            return null;
        }

        return JSON.parseArray(clearRules, RuleDto.class);
    }

    /**
     * 使用drools进行数据清洗-基于动态加载规则文件的方式
     * 清洗规则通过配置在apollo里面
     *
     * @param productInfoList 待清洗产品
     * @param ruleDtoList     清洗规则
     */
    public static void executeDynamicDrl(List<ProductInfo> productInfoList, List<RuleDto> ruleDtoList) {
        if (CollectionUtils.isEmpty(productInfoList) || CollectionUtils.isEmpty(ruleDtoList)) {
            return;
        }

        // 创建 KieSession
        KieHelper helper = new KieHelper();
        helper.addContent(getClearDataDrl(), ResourceType.DRL);
        KieSession ksession = helper.build().newKieSession();

        // 设置执行过程监听器(打印执行过程日志)
        ksession.addEventListener(new DebugAgendaEventListener());
        ksession.addEventListener(new DebugRuleRuntimeEventListener());

        // 将规则操作对象（fact）插入到session
        // 插入待清洗数据
        productInfoList.forEach(ksession::insert);
        // 插入规则
        ruleDtoList.forEach(ksession::insert);

        // 规则匹配和执行
        System.out.println("======清洗前productInfo数据:" + productInfoList.toString());
        ksession.fireAllRules();
        System.out.println("======清洗后productInfo数据:" + productInfoList.toString());

        // 销毁session
        ksession.dispose();
    }

    /**
     * 读取drl规则
     * drl规则通过配置在apollo里面，或者读取数据库（开发一个清洗规则维护界面,或者通过sql插入）
     * @return drl规则
     */
    public static String getClearDataDrl(){
        return "\n" +
                "package rules\n" +
                " \n" +
                "import com.neo.drools.entity.ProductInfo;\n" +
                "import com.neo.drools.entity.RuleDto\n" +
                "\n" +
                "// 替换\n" +
                "rule \"ClearData_replace\"\n" +
                "    dialect \"java\"\n" +
                "    no-loop true\n" +
                "    when\n" +
                "        $ruleDto:RuleDto(ruleType == RuleDto.REPLACE) and\n" +
                "        $productInfo:ProductInfo()\n" +
                "    then\n" +
                "        $productInfo.setProductName($productInfo.getProductName().replace($ruleDto.getRawValue(),$ruleDto.getStandardValue()));\n" +
                "        $productInfo.setSpec($productInfo.getSpec().replace($ruleDto.getRawValue(),$ruleDto.getStandardValue()));\n" +
                "    end\n" +
                "\n" +
                "// 置空\n" +
                "rule \"ClearData_empty\"\n" +
                "    dialect \"java\"\n" +
                "    no-loop true\n" +
                "    when\n" +
                "        $ruleDto:RuleDto(ruleType == RuleDto.EMPTY) and\n" +
                "        $productInfo:ProductInfo()\n" +
                "    then\n" +
                "        $productInfo.setProductName($productInfo.getProductName().replace($ruleDto.getRawValue(),\"\"));\n" +
                "        $productInfo.setSpec($productInfo.getSpec().replace($ruleDto.getRawValue(),\"\"));\n" +
                "    end";
    }
}
